﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Giri;

namespace Giri.L2
{
    class Message: L1.Message
    {
        private static const int MsgType = 0;
        private static const int Vias = MsgType + 1;
        private static const int FromBegin = Vias + 1;
        private static const int ToBegin = FromBegin + Identity.Size;
        private static const int ViaBegin = ToBegin + Identity.Size;
        public enum Type
        {
            Connect,            // Connects to network
            ConnectAck,         // Response to Connect

            Disconnect,         // Disconnects from peer
            DisconnectAck,      // Response to Disconnect

            SearchIdentity,     // Search for a peer
            FoundIdentity,      // Found the peer
            NotFoundIdentity,   // Didn't find the peer

            RequestIdentity,    // Send a message to a peer
            SendIdentity,       // Message for requesting identity message

            SendData,           // Sends data to peer

            Unknown             // WTF -message
        }

        public Message(L2.Message msg)
            : base(msg.GetMessageBytes())
        {
        }

        public Message(L1.Message msg)
            : base(msg)
        {
        }

        public Message(Type msgType)
            : this(msgType, null)
        {
        }

        // Transaction id is required to keep track of the current transaction
        public Message(Type msgType, Identity to)
            : base(2+((to == null?2:1)*Identity.Size))
        {

            // Byte 0 is the message type
            // Byte 1 is the number of identities carried (minimum is 2, FROM and TO, VIAs optional)
            // First identity is FROM. This identity is generated at Program -class. You can only send from yourself.
            // Second identity is TO. The receiver of this message. Not used when the message type is Connect and trying to connect to the network.
            // Other identities are VIA. These are optional and are added if necessary.

            switch (msgType)
            {
                case Type.Connect: Data(MsgType, 1); break;
                case Type.ConnectAck: Data(MsgType, 2); break;

                case Type.Disconnect: Data(MsgType, 3); break;
                case Type.DisconnectAck: Data(MsgType, 4); break;

                case Type.SearchIdentity: Data(MsgType, 5); break;
                case Type.FoundIdentity: Data(MsgType, 6); break;

                case Type.NotFoundIdentity: Data(MsgType, 7); break;
                case Type.RequestIdentity: Data(MsgType, 8); break;
                case Type.SendIdentity: Data(MsgType, 9); break;

                case Type.SendData: Data(MsgType, 10); break;

                default: Data(MsgType, 0); break;
            }

            // Setting own identity as FROM
            SetFrom(L2.Framework.Identity);

            // If TO has been give, it will be set
            if (to != null)
                Data(ToBegin, to.GetData());

        }

        public void SetFrom(Identity id)
        {
            Data(FromBegin,id.GetData());
        }

        public Identity GetFrom()
        {
            return new Identity(Data(), FromBegin);
        }

        public void SetTo(Identity id)
        {
            Data(ToBegin, id.GetData());
        }

        public Identity GetTo()
        {
            return new Identity(Data(), ToBegin);
        }

        public void AddVia(Identity addr)
        {
            int begin = Length();
            Lengthen(Identity.Size);
            Data(begin, addr.GetData());
        }

        public List<Identity> GetVias()
        {
            List<Identity> list = new List<Identity>();
            int vias = Data(Vias);
            for (int i = 0; i < vias; i++)
            {
                list.Add(new Identity(Data(), ViaBegin + (i*Identity.Size)));
            }

            return list;
        }

        public Type GetType()
        {
            switch (Data(MsgType))
            {
                case 1:     return Type.Connect;
                case 2:     return Type.ConnectAck;
                case 3:     return Type.Disconnect;
                case 4:     return Type.DisconnectAck;
                case 5:     return Type.SearchIdentity;
                case 6:     return Type.FoundIdentity;
                case 7:     return Type.NotFoundIdentity;
                case 8:     return Type.RequestIdentity;
                case 9:     return Type.SendIdentity;
                case 10:    return Type.SendData;
                default:    return Type.Unknown;
            }
        }

        public int GetTotalVias()
        {
            return Data(Vias);
        }

        public void SetData(byte[] data)
        {
            base.Data(data);
        }
    }
}
